﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Common.Eventing;


namespace Common.Eventing.InMemory
{
    /// <summary>
    /// An in-memory event broker.  Events do not get serialized.
    /// <remarks>
    /// This implementation uses the Task Parallel Library (TPL) to raise each 
    /// event handler (a.k.a. subscription) on its own background thread.
    /// </remarks>
    /// </summary>
    public class EventBroker : IEventBroker
    {
        private List<IEventHandler> _subscriptions;
        private object _subscriptionLock;


        public IEnumerable<IEventHandler> Subscriptions
        {
            get { return _subscriptions; }
        }


        public EventBroker()
        {
            _subscriptionLock = new object();
            _subscriptions = new List<IEventHandler>();
        }


        public IEnumerable<IEventHandler> GetSubscriptionsFor(Type eventType)
        {
            List<IEventHandler> allSubscriptions;

            // quickly grab a copy of all subscriptions
            lock (_subscriptionLock)
            {
                allSubscriptions = new List<IEventHandler>(_subscriptions);
            }

            // get those subscriptions whose handled type is assignable from the given event type
            return allSubscriptions.Where(handler => handler.HandledEventType.IsAssignableFrom(eventType));
        }

        public void Subscribe(IEventHandler handler)
        {
            lock (_subscriptionLock)
            {
                _subscriptions.Add(handler);
            }
        }

        public void Unsubscribe(IEventHandler handler)
        {
            lock (_subscriptionLock)
            {
                _subscriptions.Remove(handler);
            }
        }

        public void Publish(object eventMessage)
        {
            // allow each subscription to handle the event
            Parallel.ForEach<IEventHandler>(
                this.GetSubscriptionsFor(eventMessage.GetType()), 
                handler => handler.Handle(eventMessage));
        }
    }
}
